#include "OP_Delete.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

///<************************************************************************************************************>
///<************************************* region CONSTANTS DEFINITION ******************************************>
///<************************************************************************************************************>

/***************************************************************************************************************
************************************** endregion CONSTANTS DEFINITION ******************************************
****************************************************************************************************************/












/// <summary> 
/// Constructor 
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::OP_Delete::OP_Delete (vector<CIView::View*>& v)
: Operation (v) {}

/// <summary> 
/// Destructor 
/// </summary>
CIOps::OP_Delete::~OP_Delete() {
    if (status == COMPLETED) {   
        map<Geometry*, pair<vector<Vertex*>, vector<Face*> > >::iterator it;
        it = oldPrimitives.begin();

        //Geometry* g;

        //while (it != oldPrimitives.end()) {
        //    g = it->first;

        //    if (g->GetVertices().size() == 0)   //If the geometry has no vertex, it means that was removed from the scene
        //        delete it->first;               //so it has to be deleted definitely

        //    it++;
        //}

        //Deleting all vertices to delete
        for (int i=0; i<verticesToDelete.size(); i++)
            delete verticesToDelete[i];
        
        //Deleting all faces to delete
        for (int i=0; i<facesToDelete.size(); i++)
            delete facesToDelete[i];
    }
}











///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to execute directly the operation 
/// </summary>
/// <returns> The operation status </returns>
int CIOps::OP_Delete::Exec() {
    SaveOldSelection();                     //Saves the old selection status         

    DeleteSelectedPrimitives();           

    scene.DeselectAllVertices();            //Deselects all vertices (if any)

    RestoreFeedbackPosition();              //Restores the feedback position

    status = COMPLETED;

    views[0]->SetPrimitivesSelected (false);

    UpdateViews();                          //Updates the 3D views

    return status;        
}

/// <summary> 
/// Method called to undo the operation 
/// </summary>
void CIOps::OP_Delete::Undo() {
    map<Geometry*, pair<vector<Vertex*>, vector<Face*> > >::iterator it;
    it = oldPrimitives.begin();

    Geometry* g;

    while (it != oldPrimitives.end()) {
        g = it->first;

        if (g->GetVertices().size() == 0)   //If the geometry has no vertex, it means that was removed from the scene
            scene.AddGeometry (g);          //so, it has to be readded to the scene

        pair<vector<Vertex*>, vector<Face*> >& pair = it->second;

        g->SetVertices(pair.first);
        g->SetFaces(pair.second);

        g->UpdateBoundingBox();

        it++;
    }
    
    RestoreOldSelection();                              //Restores the old selection status
    
    status = UNDO;

    views[0]->SetPrimitivesSelected (true);

    UpdateViews();                          //Updates the 3D views
}

/// <summary> 
/// Method called to redo the operation 
/// </summary>
void CIOps::OP_Delete::Redo() {
    verticesToDelete.clear();
    facesToDelete.clear();
    DeleteSelectedPrimitives();     

    scene.DeselectAllVertices();            //Deselects all vertices (if any)

    RestoreFeedbackPosition();              //Restores the feedback position

    status = COMPLETED;

    views[0]->SetPrimitivesSelected (false);

    UpdateViews();                          //Updates the 3D views
}

/// <summary> 
/// Mouse Pressed event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse press event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Pressed event </param>
void CIOps::OP_Delete::MousePressEvent (CIView::View* view, QMouseEvent* e) {}

/// <summary> 
/// Mouse Released event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse release event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Released event </param>
void CIOps::OP_Delete::MouseReleaseEvent (CIView::View* view, QMouseEvent* e) {}

/// <summary> 
/// Mouse Moved event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse move event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Moved event </param>
void CIOps::OP_Delete::MouseMoveEvent (CIView::View* view, QMouseEvent* e) {}

/// <summary> 
/// Wheel event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse wheel event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Wheel event </param>
void CIOps::OP_Delete::WheelEvent (CIView::View* view, QWheelEvent* e) {}

/// <summary> 
/// Method called to delete the primitives currently selected
/// </summary>
void CIOps::OP_Delete::DeleteSelectedPrimitives() {
    switch (scene.GetSelectionMode()){
        case Geometry::VERTICES_SELECTION:
            DeleteSelectedVertices();
            break;

        case Geometry::FACES_SELECTION:
            DeleteSelectedFaces();
            break;
    }    
}

/// <summary> 
/// Method called to delete the vertices currently selected
/// </summary>
void CIOps::OP_Delete::DeleteSelectedVertices() {
    vector<Geometry*> deletedGeometries;                                        //Stores the geometry deleted from the scene

    map<const char*, Geometry*>& geometries = scene.GetGeometries();
    map<const char*, Geometry*>::iterator g = geometries.begin(); 

    bool inserted;

    while (g != geometries.end()) {                             //For each geometry
        if (g->second->hasPrimitivesSelected) {

            inserted = false;
            VertexLib& vertices = g->second->GetVertices();
            FaceLib& faces = g->second->GetFaces();
            
            for (int i=0; i<vertices.size(); i++) {                             //For each vertex of this geometry
                if (vertices[i]->selected) {                                    //if the vertex is selected, so it has to be deleted

                    if (!inserted) {
                        oldPrimitives[g->second].first = vertices;
                        oldPrimitives[g->second].second = faces;
                        inserted = true;
                    }
                    
                    verticesToDelete.push_back (vertices[i]);
                    vertices.erase (vertices.begin() + i);                      //removes the vertex from the geometry vertex library
                    i--;
                }
            }
            
            for (int i=0; i<faces.size(); i++) {                        //For each face of this geometry
                const VertexLib& faceVertices = faces[i]->vertices;
            
                //If at least a vertex of the face is selected, the face has to be deleted
                for (int j=0; j<faceVertices.size(); j++) {                         //For each vertex of this face
                    if (faceVertices[j]->selected) {                                //if the vertex is selected, the face has to be deleted
                        facesToDelete.push_back (faces[i]);
                        faces.erase (faces.begin() + i);                            //removes the face from the geometry face library
                        i--;
                
                        break;
                    }
                }
            }
            if (vertices.size() == 0)                               //If each vertex of the current geometry has been removed
                deletedGeometries.push_back (g->second);            //the geometry has to be deleted itself

            g->second->UpdateBoundingBox();
        }
        g++;
    }
    //Removing from the scene all the geometries to delete
    for (int i=0; i<deletedGeometries.size(); i++) 
        scene.RemoveGeometry (deletedGeometries[i]->GetName().c_str());
}

/// <summary> 
/// Method called to delete the faces currently selected
/// </summary>
void CIOps::OP_Delete::DeleteSelectedFaces() {
    vector<Geometry*> deletedGeometries;                                        //Stores the geometry deleted from the scene

    map<const char*, Geometry*>& geometries = scene.GetGeometries();
    map<const char*, Geometry*>::iterator g = geometries.begin();

    bool inserted;

    while (g != geometries.end()) {                             //For each geometry
        if (g->second->hasPrimitivesSelected) {
            inserted = false;
            map<Vertex*, bool> hasToBeKept;                             //Hash table to store for each vertex, if it has to be kept or deleted

            VertexLib& vertices = g->second->GetVertices();
            FaceLib& faces = g->second->GetFaces();
            
            for (int i=0; i<faces.size(); i++) {                        //For each face of this geometry
                const VertexLib& faceVertices = faces[i]->vertices;
                
                if (faces[i]->selected) {                                       //if the face is selected, it has to be deleted
                    
                    if (!inserted) {
                        oldPrimitives[g->second].first = vertices;
                        oldPrimitives[g->second].second = faces;
                        inserted = true;
                    }
                    facesToDelete.push_back (faces[i]);
                    faces.erase (faces.begin() + i);                            //removes the face from the geometry face library
                    i--;
                }
                else { //If the face is not selected, we must "save" all its vertices from deletion                                
                    //Storing info about the vertices to keep
                    for (int j=0; j<faceVertices.size(); j++) {                 //For each vertex of this face
                        hasToBeKept[faceVertices[j]] = true;                    //saves that this vertex has to be kept
                    }
                }
            }
            
            for (int i=0; i<vertices.size(); i++) {                             //For each vertex of this geometry
                if (!(hasToBeKept[vertices[i]])) {                              //if the vertex has not to be kept, it has to be deleted
                    verticesToDelete.push_back (vertices[i]);
                    vertices.erase (vertices.begin() + i);                      //removes the vertex from the geometry vertex library
                    i--;
                }
            }

            if (vertices.size() == 0)                               //If each vertex of the current geometry has been removed
                deletedGeometries.push_back (g->second);            //the geometry has to be deleted itself

            g->second->UpdateBoundingBox();
        }
        g++;
    }
    //Removing from the scene all the geometries to delete
    for (int i=0; i<deletedGeometries.size(); i++) 
        scene.RemoveGeometry (deletedGeometries[i]->GetName().c_str());
}
/***************************************************************************************************************
***************************************** endregion Utility Methods ********************************************
****************************************************************************************************************/
